package NG::Sys::Cached::DBLogger;
use strict;
use warnings;
use utf8;

use JSON::XS;

use NG::Sys::Cached::Common;
our @ISA = qw(NG::Sys::Cached::Common::Parent);

sub init {
	my $self = shift;
	$self->SUPER::init;
	unless($self->{ekey}){
		die "need ekey\n";
	}
	unless($self->{pkey}){
		die "need pkey\n";
	}
	unless($self->{fields}){
		die "need fields\n";
	}
	unless($self->{page_size}){
		die "need page_size\n";
	}
	unless($self->{table}){
		die "need table\n";
	}
	unless($self->{dbh}){
		die "need dbh\n";
	}

	@{$self->{hash_fields}}{@{$self->{fields}}} = (1) x @{$self->{fields}};
	if($self->{blobs}){
		$self->{hash_blobs} = { map {$_=>1} $self->check_fields_ary($self->{blobs}) };
	}else{
		$self->{hash_blobs} = {};
	}

	$self->{record_class} ||= 'NG::Sys::Cached::DBLogger::Record';

	$self->{sth}{count} = $self->{dbh}->prepare("
		select max(`".$self->{ekey}."`) from `".$self->{table}."`
		where `".$self->{pkey}."`=?");
	$self->{sth}{insert} = $self->{dbh}->prepare("insert into `".$self->{table}."` (".
					"`".$self->{pkey}."`,`".$self->{ekey}."`,".
					join(", ",map {"`".$_."`" } @{$self->{fields}}).
					") values (?,?,".
					join(", ",("?") x  @{$self->{fields}}).
					")");
	$self->{sth}{read} = $self->{dbh}->prepare("
		select * from `".$self->{table}."` where `".$self->{pkey}."`=?
		and `".$self->{ekey}."` between ? and ?
		order by `".$self->{pkey}."` asc,`".$self->{ekey}."` asc
		");
}
sub check_field {
	my $self = shift;
	my $key = shift;
	return exists $self->{hash_fields}{$key} || die "unexists key'".$key."' checked\n";
}
sub check_fields_ary {
	my ($self,$keys) = @_;
	my $h = $self->{hash_fields};
	exists $h->{$_} || die "unexists key'".$_."' checked\n" for @$keys;
	return @$keys;
}
sub need_create {
	return undef;
}
sub get_obj {
	my ($self,$id) = @_;
	die "Bad id='$id'" unless $id and 0 < $id and !ref $id;
	return bless {parent=>$self,id=>$id},$self->{record_class};
}
sub _read_data {
	my $self= shift;
	my $id = shift;
	$self->{sth}{count}->execute($id);
	my $count = $self->{sth}{count}->fetchrow_array() || 0;
	return $count;
}
sub _get_length {
	my $self = shift;
	my $id = shift;
	my $data;
	if(defined ($data = $self->context->{$id})){
		$self->count_action("context");
		return $data;
	}
	if(defined($data = $self->{cache}->get($id))){
		$self->count_action("cache");
		$self->context->{$id} = $data;
		return $data;
	}
	$data = $self->_read_data($id);
	$self->{cache}->set($id,$data);
	$self->context->{$id} = $data;
	return $data;
}
sub insert {
	my $self = shift;
	my $id = shift;
	my $row = shift;
	my $count = $self->_get_length($id);
	if(scalar %{$self->{hash_blobs}}){
		for(@{$self->{blobs}}){
			defined($row->{$_}) and $row->{$_} = encode_json($row->{$_});
		}
	}
	my $entry = $count + 1;
	$self->{sth}{insert}->execute($id,$entry,@{$row}{@{$self->{fields}}});
	$self->clear_cache($id);
	return $entry;
}
sub _get_page {
	my $self = shift;
	my $id = shift;
	my $page = shift;
	my $sc = ($page - 1) * $self->{page_size};
	my $ec = $sc + $self->{page_size};
	
	$self->{sth}{read}->execute($id,$sc+1,$ec);
	my $list = $self->{sth}{read}->fetchall_arrayref({});
	if(scalar %{$self->{hash_blobs}}){
		for my $row(@$list){
			for(@{$self->{blobs}}){
				defined $row->{$_} and $row->{$_} = decode_json($row->{$_});
			}
		}
	}
	return $list;
}
sub _get_row {
	my $self = shift;
	my $id = shift;
	my $entry = shift;
	
	$self->{sth}{read}->execute($id,$entry,$entry);
	my $list = $self->{sth}{read}->fetchall_arrayref({});
	if(scalar %{$self->{hash_blobs}}){
		for my $row(@$list){
			for(@{$self->{blobs}}){
				defined $row->{$_} and $row->{$_} = decode_json($row->{$_});
			}
		}
	}
	return $list->[0];
}

package NG::Sys::Cached::DBLogger::Record;
use strict;
use warnings;
use utf8;

use NG::Sys::Cached::Common;
our @ISA = qw(NG::Sys::Cached::Common::Child);

sub obj_id {
	my ($self) = @_;
	return $self->{id};
}
sub length {
	my $self = shift;
	return $self->{parent}->_get_length($self->{id});
}
sub pages {
	my $self = shift;
	my $l = $self->length;
	if($l > 0){
		$l--;
	}else{
		return 0;
	}
	return int($l / $self->{parent}{page_size} + 1);
}
sub insert {
	my $self = shift;
	my $row = shift;
	$self->{parent}->insert($self->{id},$row);
}
sub get_page {
	my $self = shift;
	my $page = shift;
	my $tp = $self->pages;
	unless($page){
		$page = $tp;
	}
	if($page < 1){
		return [];
	}elsif($page > $tp){
		return [];
	}
	return $self->{parent}->_get_page($self->{id},$page);
}
sub remove{}

sub get_row {
	my $self = shift;
	my $entry = shift;
	return $self->{parent}->_get_row($self->{id},$entry);
}

1;
